package util

import (
	"bufio"
	"fmt"
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

type FInfo = TarFileInfo

func CreateAllDir(path string) error {
	p, err := ExpandTilde(path)
	if err != nil {
		return err
	}
	return os.MkdirAll(p, os.ModePerm)
}

func IsPathExists(path string) bool {
	p, err := ExpandTilde(path)
	if err != nil {
		return false
	}

	//_, err = os.Stat(p)
	//if os.IsNotExist(err) {
	//	return false
	//}
	//return true
	_, error := os.Open(p) // For read access.
	return error == nil
}

func GetAllFolders(path string) ([]string, error) {
	fileNames := make([]string, 0)

	files, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if file.IsDir() {
			fileNames = append(fileNames, file.Name())
		}
	}

	return fileNames, nil
}

func GetAllFiles(path string, recursive bool) ([]string, error) {
	fileNames := make([]string, 0)

	files, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if !file.IsDir() {
			fileNames = append(fileNames, file.Name())
		}
	}

	return fileNames, nil
}

func ExpandTilde(path string) (string, error) {
	if path[:2] == "~/" {
		u, err := user.Current()
		if err != nil {
			return "", err
		}
		return filepath.Join(u.HomeDir, path[2:]), nil
	}
	return path, nil
}

func CoyDir(src string, dst string) error {
	var err error
	var fds []os.FileInfo
	var srcinfo os.FileInfo

	if srcinfo, err = os.Stat(src); err != nil {
		return err
	}

	if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
		return err
	}

	if fds, err = ioutil.ReadDir(src); err != nil {
		return err
	}
	for _, fd := range fds {
		srcfp := path.Join(src, fd.Name())
		dstfp := path.Join(dst, fd.Name())

		if fd.IsDir() {
			if err = CoyDir(srcfp, dstfp); err != nil {
				fmt.Println(err)
			}
		} else {
			if err = CopyFile(srcfp, dstfp); err != nil {
				fmt.Println(err)
			}
		}
	}
	return nil
}

func CopyFile(src, dst string) error {
	var err error
	var srcfd *os.File
	var dstfd *os.File
	var srcinfo os.FileInfo

	if srcfd, err = os.Open(src); err != nil {
		return err
	}
	defer srcfd.Close()

	if dstfd, err = os.Create(dst); err != nil {
		return err
	}
	defer dstfd.Close()

	if _, err = io.Copy(dstfd, srcfd); err != nil {
		return err
	}
	if srcinfo, err = os.Stat(src); err != nil {
		return err
	}
	return os.Chmod(dst, srcinfo.Mode())
}

func ListFiles(inpath string) (filesInfo []FInfo, err error) {
	if err := filepath.Walk(inpath,
		func(path string, finfo os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if path == inpath {
				return nil
			}
			//trimmedPath := strings.TrimLeft(path, inpath+"/")
			trimmedPath := path
			inpath_len := len(inpath)
			if ind := strings.Index(path, inpath); ind != -1 {
				trimmedPath = path[inpath_len+1:]
			}

			info := FInfo{
				Name:  trimmedPath,
				Mode:  uint32(finfo.Mode()),
				Size:  finfo.Size(),
				IsDir: finfo.IsDir(),
			}
			filesInfo = append(filesInfo, info)

			return nil
		}); err != nil {
		filesInfo = nil
		return filesInfo, err
	}

	return
}

func Find(dirname, pattern string) (files []string, err error) {
	//files, err := os.ReadDir(dirname)
	//if err != nil {
	//	return
	//}
	//
	//for _, file := range files {
	//	match, err := filepath.Match(pattern, file.Name())
	//	if err != nil {
	//		return nil, err
	//	}
	//	if match {
	//		fs, _ := file.Info()
	//		matchingFiles = append(matchingFiles, fs)
	//	}
	//}
	////fmt.Println("Files:", matchingFiles)
	//return

	err = filepath.Walk(dirname, func(path string, info fs.FileInfo, err error) error {
		if err != nil {
			fmt.Println(err)
			return nil
		}

		reg, err2 := regexp.Compile(pattern)
		if err2 != nil {
			return err2
		}

		if reg.MatchString(info.Name()) {
			files = append(files, path)
		}

		return nil
	})
	return
}

func ReadLines(filePath string, offset int64, line int) (lines []string, fileSize int64, next int64, err error) {
	file, err := os.Open(filePath)
	if err != nil {
		return
	}
	defer file.Close()

	fileInfo, err := file.Stat()
	if err != nil {
		return
	}
	fileSize = fileInfo.Size()

	_, err = file.Seek(offset, io.SeekStart)
	if err != nil {
		return
	}
	next = offset

	scanner := bufio.NewScanner(file)
	lineIndex := 0
	for scanner.Scan() {
		if lineIndex == line {
			break
		}
		bytes := scanner.Bytes()
		next = next + int64(len(bytes)) + 1

		lines = append(lines, string(bytes))
		lineIndex++
	}
	err = scanner.Err()

	return
}
